import poser, os, math, re

os.chdir( os.path.dirname(poser.AppLocation()) )

def addGwoProp(pp2):
	scene = poser.Scene()
	scene.LoadLibraryProp(pp2)
	gwoProp = scene.CurrentActor()
	target = scene.CurrentCamera()
	gwoProp.PointAt(target)
	return gwoProp

def addGwoClip():
	gwoClipPath = os.path.join("Runtime","Python","PoseWorks","GlowWorm","Data","gwoClip.pp2")
	return addGwoProp( gwoClipPath )

def addGwoStart():
	for actor in poser.Scene().Actors():
		if re.search("GWO_DEPTHCUE_START", actor.InternalName()):
			return None
	gwoStartPath = os.path.join("Runtime","Python","PoseWorks","GlowWorm","Data","gwoStart.pp2")
	return addGwoProp( gwoStartPath )

def addGwoEnd():
	for actor in poser.Scene().Actors():
		if re.search("GWO_DEPTHCUE_END", actor.InternalName()):
			return None
	gwoEndPath = os.path.join("Runtime","Python","PoseWorks","GlowWorm","Data","gwoEnd.pp2")
	return addGwoProp( gwoEndPath )

def pVert(vert):
	return (vert.X(), vert.Y(), vert.Z())

def pAdd(p0, p1):
	return (p0[0] + p1[0], p0[1] + p1[1], p0[2] + p1[2])

def pMul(p0, p1):
	return (p0[0] * p1[0], p0[1] * p1[1], p0[2] * p1[2])

def pNeg(p0):
	return (-p0[0], -p0[1], -p0[2])

def pSum(p0):
	return p0[0] + p0[1] + p0[2]

def pDist(p0, p1):
	p2 = pAdd(p0, pNeg(p1))
	return math.sqrt( pSum(pMul(p2,p2)) )

def getDist(p2, actor):
	p1 = actor.WorldDisplacement()
	p3 = actor.Origin()
	d = (-p3[0] + p2[0]-p1[0], -p3[1] + p2[1]-p1[1], -p3[2] + p2[2]-p1[2])
	return math.sqrt (d[0]*d[0] + d[1]*d[1] + d[2]*d[2])

def getActorNearFar(pCam, actor):
	near, far = 999999, 0
	p1 = actor.WorldDisplacement()
	geom = actor.Geometry()
	if not geom:
		return None
	for v in geom.WorldVertices():
		d = pDist(pCam, pVert(v))
		if d > far: far = d
		elif d < near: near = d
	return near, far

def getNearFar(scene):
	near, far = 999999, 0
	pCam = scene.CurrentCamera().LocalDisplacement()
	for actor in scene.Actors():
		if not actor.IsProp() and not actor.IsBodyPart():
			continue
		if not actor.Visible() or actor.InternalName() == "FocusDistanceControl" or actor.IsDeformer() or actor.IsBase() or actor.IsZone():
			continue
		nf = getActorNearFar(pCam, actor)
		if not nf: continue
		n, f = nf
		if n < near: near = n
		if f > far: far = f
	return near, far

def getDistFromCam(actor, camera):
	pCam = camera.LocalDisplacement()
	pAct = actor.WorldDisplacement()
	return pDist(pCam, pAct)

def depthCue(scene):
	cam = scene.CurrentCamera()
	start, end = -1, -1
	for actor in scene.Actors():
		if "GWO_DEPTHCUE_START" == actor.InternalName():
			start = getDistFromCam(cam, actor)
		elif "GWO_DEPTHCUE_END" == actor.InternalName():
			end = getDistFromCam(cam, actor)
	if start == -1 or end == -1:
		#near, far = getNearFar(scene)
		if start == -1: start = 0.969
		if end == -1: end = 5.814
	as = scene.AtmosphereShaderTree().Node(0)
	as.InputByInternalName("DepthCue_StartDistance").SetFloat(start / 0.009690)
	as.InputByInternalName("DepthCue_EndDistance").SetFloat(end / 0.009690)
	scene.AtmosphereShaderTree().UpdatePreview()

def clipSceneOld(scene, clipNear=None, clipFar=None, invisibles=[]):
	pCam = scene.CurrentCamera().LocalDisplacement()
	if not clipNear:
		hither = 0
	else:
		hither = clipNear #getDist(pCam, clipNear)
	if not clipFar:
		yon = 999999
	else:
		yon = clipFar #getDist(pCam, clipFar)
	for actor in scene.Actors():
		nf = getActorNearFar(pCam, actor)
		if not nf: continue
		n, f = nf
		if (hither < f <= yon):
			if not actor.InternalName() in invisibles:
				actor.SetVisibleInRender(1)
		else:
			actor.SetVisibleInRender(0)

class vPoint:
	def __init__(self, vert):
		self.x, self.y, self.z = vert.X(), vert.Y(), vert.Z()
	def X(self):
		return self.x
	def Y(self):
		return self.y
	def Z(self):
		return self.z

def getBoundingBox(wvs):
	min = vPoint(wvs[0])
	max = min
	for v in wvs[1:]:
		if v.X() < min.x:
			min.x = v.X()
		elif v.X() > max.x:
			max.x = v.X()
		if v.Y() < min.y:
			min.y = v.Y()
		elif v.Y() > max.y:
			max.y = v.Y()
		if v.Z() < min.z:
			min.z = v.Z()
		elif v.Z() > max.z:
			max.z = v.Z()
	return min, max

def clipScene(scene, clipNear=None, clipFar=None, invisibles=[]):
	if not clipNear: hither = 0
	else: hither = clipNear
	if not clipFar: yon = 99999
	else: yon = clipFar
	pCam = scene.CurrentCamera().LocalDisplacement()
	for actor in scene.Actors():
		if not actor.IsProp() and not actor.IsBodyPart():
			continue
		if not actor.Visible() or actor.InternalName() == "UNIVERSE" or actor.InternalName() == "FocusDistanceControl" or actor.IsDeformer() or actor.IsBase() or actor.IsZone():
			continue
		geom = actor.Geometry()
		if not hasattr(geom, "WorldVertices"): continue
		near, far = getBoundingBox(geom.WorldVertices())
		if (hither < pDist(pCam, (far.x, far.y, far.z)) <= yon):
			if not actor.InternalName() in invisibles:
				actor.SetVisibleInRender(1)
		else:
			actor.SetVisibleInRender(0)

def setHitherYon(clipNear=None, clipFar=None):
	ignoreHither, ignoreYon = 0, 0
	if not clipNear and not clipFar:
		return None
	elif not clipNear:
		ignoreHither = 1
	elif not clipFar:
		ignoreYon = 1
	cam = poser.Scene().CurrentCamera()
	hither = cam.ParameterByCode(poser.kParmCodeHITHER)
	yon = cam.ParameterByCode(poser.kParmCodeYON)
	if ignoreHither:
		hither.SetValue(0.048)
	else:
		hither.SetValue( getDist(cam, clipNear) )
	if ignoreYon:
		yon.SetValue(100.0)
	else:
		yon.SetValue( getDist(cam, clipFar) )

def getClips(scene):
	pCam = scene.CurrentCamera().LocalDisplacement()
	gwoClips = {}
	for actor in scene.Actors():
		if re.search('GWO_CLIPPING_PLANE', actor.InternalName()):
			gwoClips[getDist(pCam,actor)] = actor
	clips = gwoClips.keys()
	clips.sort()
	clips.insert(0, 0)
	clips.append(999999)
	tups, i = [], 0
	for clip in clips[:-1]:
		tups.append( (clip, clips[i+1]) )
		i = i + 1
	return tups

def getInvisibles(scene):
	invisibles = []
	for actor in scene.Actors():
		if not actor.VisibleInRender(): invisibles.append( actor.InternalName() )
	return invisibles

def restoreVisibility(scene, invisibles):
	for actor in scene.Actors():
		if not actor.InternalName() in invisibles: actor.SetVisibleInRender(1)

if __name__ == '__main__':
	#far = addGwoClip()

	scene = poser.Scene()
	clips = getClips(scene)
	invisibles = getInvisibles(scene)

	for near, far in clips:
		clipId = clips.index( (near,far) )
		clipScene(scene, clipNear=near, clipFar=far, invisibles=invisibles)
		scene.Render()

	restoreVisibility(scene, invisibles)

pass